/**
 ******************************************************************************
 * @file    utils.h
 * @author  software Team
 * @brief   This file provides all the headers of utils functions.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2020 Hyseim. Co., Ltd.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __UTILS_H__
#define __UTILS_H__

/* Includes ------------------------------------------------------------------*/
#include "string.h"
#include "chip_define.h"
#include "typedefine.h"
#include "csr_reg.h"

/* Exported types ------------------------------------------------------------*/
typedef enum
{
    RESET = 0,
    SET = !RESET
} FlagStatus_t,
    ITStatus_t;

typedef enum
{
    DISABLE = 0,
    ENABLE = !DISABLE
} FunctionalState_t;

typedef enum
{
    WRITE = 0,
    READ  = !WRITE
} I2C_MasterDirection_t;
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))

typedef enum
{
    INACTIVE = 0,
    ACTIVE = !INACTIVE
} SignalState_t;

#ifdef __cplusplus
#define __I volatile /*!< Defines 'read only' permissions */
#else
#define __I volatile const /*!< Defines 'read only' permissions */
#endif
#define __O volatile  /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
/**
 * @brief  Status structures definition
 */
typedef enum
{
    STA_OK = 0x00U,
    STA_ERROR = 0x01U,
    STA_BUSY = 0x02U,
    STA_TIMEOUT = 0x03U
} Status_t;

typedef enum
{
    SUCCESS = 0U,
    ERROR = !SUCCESS
} ErrStatus_t;

typedef enum
{
    TEST_OK = 0,
    TEST_FAILD = -1,
} TestStatus_t;

/**
 * @brief  Lock structures definition
 */
typedef enum
{
    UNLOCKED = 0x00U,
    LOCKED = 0x01U
} Lock_t;

/* Exported constants --------------------------------------------------------*/

/* Exported macro ------------------------------------------------------------*/
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
#define READ_BIT(REG, BIT) ((REG) & (BIT))
#define CLEAR_REG(REG) ((REG) = (0x0))
#define WRITE_REG(REG, VAL) ((REG) = (VAL))
#define READ_REG(REG) ((REG))
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))

// TODO assert_param
#define assert_param(x)

#define im_write8(addr, val_)   (*(volatile unsigned char *)(long)(addr) = (val_))
#define im_write16(addr, val_)  (*(volatile unsigned short *)(long)(addr) = (val_))
#define im_write32(addr, val_)  (*(volatile unsigned int *)(long)(addr) = (val_))
#define im_write64(addr, val_)  (*(volatile unsigned long long *)(long)(addr) = (val_))
#define im_write(addr, val_)    im_write32(addr, val_)

#define im_read8(addr)          (*(volatile unsigned char *)(long)(addr))
#define im_read16(addr)         (*(volatile unsigned short *)(long)(addr))
#define im_read32(addr)         (*(volatile unsigned int *)(long)(addr))
#define im_read64(addr)         (*(volatile unsigned long long *)(long)(addr))
#define im_read(addr)           im_read32(addr)

#define ARR_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define BIT(n) (1UL << (n))

static inline void putc_stdout(char c)
{
    *(volatile unsigned int *)(0x70008000) = (c); // 0x70008000 is REG_BASE_SIMDBG
}

static inline void putc_uart(char c)
{
    extern volatile const unsigned int * pprint_UartLsr;
    extern volatile unsigned int * pprint_UartThr;
    while (!(*pprint_UartLsr & 0x20))
        ;
    *pprint_UartThr = c;
}

static inline int getc_uart(void)
{
    extern volatile const unsigned int * pprint_UartLsr;
    extern volatile const unsigned int * pprint_UartRbr;
    while (!(*pprint_UartLsr & BIT(0) ))
        ;
    return (*pprint_UartRbr);
}

// start hart1
static inline void start_hart1(void)
{
    asm volatile("csrwi 0x7fc, 3");
}

static inline void it1_set_cnt(unsigned int cnt)
{
    CSR_SETEX(CSR_MITCNT1, cnt);
}
static inline unsigned int it1_get_cnt(void)
{
    unsigned int cnt;
    CSR_GETEX(CSR_MITCNT1, cnt);
    return cnt;
}
static inline void it0_set_cnt(unsigned int cnt)
{
    CSR_SETEX(CSR_MITCNT0, cnt);
}
static inline unsigned int it0_get_cnt(void)
{
    unsigned int cnt;
    CSR_GETEX(CSR_MITCNT0, cnt);
    return cnt;
}
static inline void cgc_clrb(unsigned int bit)
{
    CSRCEX(CSR_MCGC, bit);
}
static inline void cgc_setb(unsigned int bit)
{
    CSRSEX(CSR_MCGC, bit);
}
static inline unsigned int hart_id(void)
{
    unsigned id = 0;
    asm volatile("csrr %[op0], mhartid" : [op0] "=r"(id) : :);
    return id;
}

/*
 wakeup source from halt:
 1.PMU run request
 2.Highest-priority external interrupt (mhwakeup[0/1] signal from PIC) and interrupts are enabled (per thread)
 3.Software interrupt (per thread)
 4.Timer interrupt (per thread)
 5.Internal timer interrupt (per thread)
 6.Non-maskable interrupt (NMI) (nmi_int signal, if thread is selected to handle NMI (see Section 4.4.3))
 */
static inline void hart_halt(void)
{
    asm volatile("fence.i");
    CSRSEX(CSR_MPMC, 1);
}
// pause is decremented by 1 for each core clock cycle. Execution
// continues either when pause is 0 or any interrupt is received.
static inline void hart_pause(unsigned cycle)
{
    asm volatile("csrw 0x7c2, %[op0]" : : [op0] "r"(cycle) :);
}

static inline void mtx_lock(uint32_t *mtx)
{
    uint32_t t0 = 1;
    asm volatile("1: amoswap.w.aq %[t0], %[t0], 0(%[m0])\r\n"
                 "bnez %[t0], 1b" : [t0] "+r"(t0) : [m0] "r"(mtx) :);
}
static inline void mtx_unlock(uint32_t *mtx)
{
    asm volatile("amoswap.w.rl x0, x0, 0(%[m0])" ::[m0] "r"(mtx) :);
}
/* For getting core ID. */
static inline int get_core_id()
{
    return 0;
}

// get number of cores
static inline int get_core_num()
{
    return 0;
}

static inline void clrb_mfdc(unsigned n)
{
    asm volatile("fence.i");
    CSRC(0x7F9, n);
    asm volatile("fence.i");
}
static inline void setb_mfdc(unsigned n)
{
    asm volatile("fence.i");
    CSRS(0x7F9, n);
    asm volatile("fence.i");
}
static inline unsigned get_mfdc(void)
{
    unsigned n = 0;
    asm volatile("fence.i");
    CSR_GET(0x7F9, n);
    asm volatile("fence.i");
    return n;
}
static inline void set_mfdc(unsigned n)
{
    asm volatile("fence.i");
    CSR_SET(0x7F9, n);
    asm volatile("fence.i");
}

static inline void t_set_buf(unsigned int *buf)
{
    CSR_SETEX(CSR_MSCRATCH, buf);
}
static inline unsigned int *t_get_buf(void)
{
    unsigned int *addr = 0;
    CSR_GETEX(CSR_MSCRATCH, addr);
    return addr;
}
static inline void cpu_delay(unsigned int delay)
{
    volatile unsigned int cnt = delay;
    while (cnt--)
        ;
}

//TODO
#define BOOT_CPU_CLK 1

// The delay function is imprecise under 12M clock
static inline void boot_us_delay(uint32_t delay)
{
    uint32_t cnt = delay * BOOT_CPU_CLK;
    uint32_t start = 0;
    uint32_t end = 0;
    CSR_GETEX(CSR_MITCNT0, start);
    do
    {
        CSR_GETEX(CSR_MITCNT0, end);
    } while ((end - start) < cnt);
}

static inline uint32_t boot_get_timer(uint32_t init_time)
{
    uint32_t start = 0;
    CSR_GETEX(CSR_MITCNT0, start);
    return start - init_time;
}

static inline void set_boot_addr(unsigned int addr)
{
    im_write32(CRG_AO_BASE + 0, BIT(7) | BIT(23));
    im_write32(CRG_AO_BASE + 0x88, addr);
}
static inline void cpu_scratch_set(unsigned int addr)
{
    im_write32(CRG_AO_BASE + 0x11c, addr);
}
static inline unsigned int cpu_scratch_get(void)
{
    return im_read32(CRG_AO_BASE + 0x11c);
}
static inline void cpu_reset(void)
{
    im_write32(CRG_BASE + 0x104, BIT(0) | BIT(16));
}
static inline void soc_reset(void)
{
    im_write32(CRG_AO_BASE + 0x8, im_read32(CRG_AO_BASE + 0x8) | BIT(11));
    im_write32(CRG_BASE + 0x104, 0xFFFF9527);
}

static inline void boot_soc_reset(void)
{
    set_boot_addr(BOOTROM_BASE);
    im_write32(CRG_AO_BASE + 0x8, im_read32(CRG_AO_BASE + 0x8) | BIT(11));
    im_write32(CRG_BASE + 0x104, 0xFFFF9527);
}

#define INT_NEST_PUSH_CONTEXT                                       \
    volatile uint32_t mstatus, mepc, mie, thr;                      \
    CSR_GETEX(CSR_MSTATUS, mstatus);                                \
    CSR_GETEX(CSR_MEPC, mepc);                                      \
    CSR_GETEX(CSR_MIE, mie);                                        \
    thr = ext_int_get_cur_priority_threshold();                     \
    ext_int_set_cur_priority_threshold(ext_int_get_cur_priority()); \
    CSRSEX(CSR_MSTATUS, BIT(3));

#define INT_NEST_POP_CONTEXT         \
    CSR_SETEX(CSR_MSTATUS, mstatus); \
    CSR_SETEX(CSR_MEPC, mepc);       \
    CSR_SETEX(CSR_MIE, mie);         \
    ext_int_set_cur_priority_threshold(thr);

/* Exported functions ------------------------------------------------------- */
#endif

/*******************(C)COPYRIGHT 2020 Hyseim ***********END OF FILE************/
